#include <buola/gl.h>

#include <buola/image/ctextlayout.h>
#include <buola/container/cautovector.h>
#include <buola/image/canyimage.h>
#include <buola/image/rasterizer/cstroker.h>
#include <buola/image/font/cfontface.h>
#include <buola/image/font/cscaledfont.h>

#include <buola/gl.h>
#include <buola/gl/cglyphcache.h>

#include <buola/gl/copenglgc.h>
#include <buola/gl/ctexturesurface.h>
#include <buola/gl/cvertexarray.h>
#include <buola/utility/cregion.h>

#include "shaders/sGlyphFragment.h"
#include "shaders/sGlyphVertex.h"
#include "shaders/sSolidFragment.h"
#include "shaders/sSolidVertex.h"
#include "shaders/sLinearFragment.h"
#include "shaders/sLinearVertex.h"
#include "shaders/sRadialFragment.h"
#include "shaders/sImageFragment.h"
#include "shaders/sImageVertex.h"

namespace buola { namespace gl {

typedef CPoint<GLfloat> CPoint_gl;
typedef CRect<GLfloat> CRect_gl;
#if 0
///for now this is a very simple class that allows for adding convex polygons
class CVertexArray
{
public:
    CVertexArray()
        :   mMin(nUndefined)
        ,   mMax(nUndefined)
    {}

    void AddVertex(const CPoint_f &pP)
    {
        mVertices.emplace_back(pP);
    }

    void AddPath(const img::CPath &pPath,float pCurveInverseScale,bool pOutline=true)
    {
        const auto &lElements=pPath.Elements();

        if(mMin.Undefined())
            mMax=mMin=CPoint_f(pPath.Elements()[0].mPoint);

        auto lElemIt=lElements.begin();
        
        int lLastMoveTo=mVertices.size();
        mVertices.emplace_back(*(lPointIt++));

        for(++lOpIt;lOpIt!=lOps.end();++lOpIt)
        {
            switch(*lOpIt)
            {
            case img::CPath::OP_MOVE:
                if(!pOutline)
                    AddClosingLine(lLastMoveTo);
                mStops.push_back(mVertices.size());
                lLastMoveTo=mVertices.size();
                AddVertex(CPoint_f(*(lPointIt++)));
                break;
            case img::CPath::OP_LINE:
                AddVertex(CPoint_f(*(lPointIt++)));
                break;
            case img::CPath::OP_CURVE:
                ///\todo
                lPointIt+=3;
                break;
            case img::CPath::OP_CLOSE:
                AddVertex(CPoint_f(*(lPointIt++)));
                break;
            }
        }

        if(!pOutline)
            AddClosingLine(lLastMoveTo);
        mStops.push_back(mVertices.size());
    }

    void Clear()
    {
        mVertices.clear();
        mStops.clear();
        mMin=nUndefined;
        mMax=nUndefined;
    }

    const std::vector<CPoint_gl> &Vertices() const {    return mVertices;   }
    const std::vector<int> &Stops() const {    return mStops;   }

    CRect_gl BoundingRect() const    {   return CRect_gl(mMin,mMax);    }

public:
    void AddClosingLine(int pIndex)
    {
        if(mVertices.back()!=mVertices[pIndex])
        {
            mVertices.emplace_back(mVertices[pIndex]);
        }
    }

private:
    std::vector<CPoint_gl> mVertices;
    std::vector<int> mStops;

    CPoint_gl mMin,mMax;
};
#endif    
gl::CGlyphCache *COpenGLGC::mGlyphCache=nullptr;

enum ECompositeBounded
{
    BOUNDED_NONE=0,
    BOUNDED_BY_SOURCE=(1<<0),
    BOUNDED_BY_MASK=(1<<1)
};

struct CCompositeExtents
{
    CRect_i mDestination;
    CRect_i mSource;
    CRect_i mMask;
    
    CRect_i mUnbounded; //destination IN clip
    CRect_i mBounded; //source IN mask IN unbounded
    ECompositeBounded mIsBounded;
/*    
    cairo_rectangle_int_t source_sample_area;
    cairo_rectangle_int_t mask_sample_area;

    cairo_pattern_union_t source_pattern;
    cairo_pattern_union_t mask_pattern;
    const cairo_pattern_t *original_source_pattern;
    const cairo_pattern_t *original_mask_pattern;

    cairo_clip_t *clip; // clip will be reduced to the minimal container 
*/
    
};

COpenGLGC::COpenGLGC(CContext *pContext,bool pOwn)
    :   mContext(pContext)
    ,   mOwnContext(pOwn)
    ,   mClip(false)
{
    ResetState();
}

COpenGLGC::~COpenGLGC()
{
    if(mOwnContext)
        delete mContext;
}

#if 0
void COpenGLGC::SetSource(const img::CColor &pColor)
{
    mState.mColor=pColor;
    gl::color(pColor);
}

void COpenGLGC::SetFont(img::PFont pFont)
{
    mState.mFont=pFont;
}

void COpenGLGC::Translate(const CPoint_d &pPos)
{
    mState.mCTM.Translate(pPos);
}

void COpenGLGC::Rotate(double pAngle)
{
    mState.mCTM.Rotate(pAngle);
}

void COpenGLGC::Scale(const CSize_d &pScale)
{
    mState.mCTM.Scale(pScale);
}

void COpenGLGC::MultMatrix(const mat::CMat23d &pMatrix)
{
    msg_warn() << "not implemented COpenGLGC::MultMatrix\n";
}
#endif

static const int sPrimToGLLine[]={  GL_LINES,GL_LINE_STRIP,GL_LINE_LOOP };

class CGLVertexRenderer
{
public:
    CGLVertexRenderer(std::vector<float> &pV)
        :   mV(pV)
    {}
    
    void RenderPixel(int x,int y,uint8_t pCover)
    {
        float lX=x;
        float lY=y;
        float lCover=pCover/255.0f;
        mV.push_back(lX);
        mV.push_back(lY);
        mV.push_back(lCover);
        mV.push_back(lX+1);
        mV.push_back(lY);
        mV.push_back(lCover);
        mV.push_back(lX);
        mV.push_back(lY+1);
        mV.push_back(lCover);
        
        mV.push_back(lX+1);
        mV.push_back(lY);
        mV.push_back(lCover);
        mV.push_back(lX+1);
        mV.push_back(lY+1);
        mV.push_back(lCover);
        mV.push_back(lX);
        mV.push_back(lY+1);
        mV.push_back(lCover);
    }

    void RenderHLine(int x,int y,int w,uint8_t pCover)
    {
        float lX=x;
        float lY=y;
        float lR=lX+w;
        float lCover=pCover/255.0f;
        mV.push_back(lX);
        mV.push_back(lY);
        mV.push_back(lCover);
        mV.push_back(lR);
        mV.push_back(lY);
        mV.push_back(lCover);
        mV.push_back(lX);
        mV.push_back(lY+1);
        mV.push_back(lCover);
        
        mV.push_back(lR);
        mV.push_back(lY);
        mV.push_back(lCover);
        mV.push_back(lR);
        mV.push_back(lY+1);
        mV.push_back(lCover);
        mV.push_back(lX);
        mV.push_back(lY+1);
        mV.push_back(lCover);
    }

private:
    std::vector<float> &mV;
};

class CGLVertexLineRenderer
{
public:
    CGLVertexLineRenderer(std::vector<float> &pV)
        :   mV(pV)
    {}

    void RenderPixel(int x,int y,uint8_t pCover)
    {
        float lX=x;
        float lY=y+0.6f;
        float lCover=pCover/255.0f;
        mV.push_back(lX);
        mV.push_back(lY);
        mV.push_back(lCover);
        mV.push_back(lX+1);
        mV.push_back(lY);
        mV.push_back(lCover);
    }

    void RenderHLine(int x,int y,int w,uint8_t pCover)
    {
        float lX=x;
        float lY=y+0.6f;
        float lR=lX+w;
        float lCover=pCover/255.0f;
        mV.push_back(lX);
        mV.push_back(lY);
        mV.push_back(lCover);
        mV.push_back(lR);
        mV.push_back(lY);
        mV.push_back(lCover);
    }

private:
    std::vector<float> &mV;
};

void COpenGLGC::PrepareLinear()
{
    const img::CLinearGradient &lGradient=Source().LinearGradient();

    mGradientTexture.Create1D(lGradient);
    mLinearProgram.Use();
    uLinearOrigin.SetValue(lGradient.Point1());
    CPoint_d lDirection=lGradient.Point2()-lGradient.Point1();
    double lLength=norm(lDirection);
    uLinearDirection.SetValue(lDirection/lLength);
    uLinearLength.SetValue((float)lLength);
}

void COpenGLGC::PrepareRadial()
{
    const img::CRadialGradient &lGradient=Source().RadialGradient();

    mGradientTexture.Create1D(lGradient);
    mRadialProgram.Use();
    CPoint_d lDiff=lGradient.Point2()-lGradient.Point1();
    uRadial1.SetValue(mat::CVec3f(lGradient.Point1().x,lGradient.Point1().y,lGradient.Radius1()));
    uRadialD.SetValue(mat::CVec3f(lDiff.x,lDiff.y,lGradient.Radius2()-lGradient.Radius1()));
    uRadialA.SetValue((float)(dot(lGradient.Point1(),lDiff)-sq(lGradient.Radius2()-lGradient.Radius1())));
}

void COpenGLGC::PrepareProgram()
{
    switch(Source().Type())
    {
    case img::CPattern::SOLID:
        mSolidProgram.Use();
        mSolidColor.SetValue(Source().Color());
        break;
    case img::CPattern::LINEAR_GRADIENT:
        PrepareLinear();
        break;
    case img::CPattern::RADIAL_GRADIENT:
        PrepareRadial();
        break;
    case img::CPattern::NONE:
    case img::CPattern::SURFACE:
        throw XInvalid("unsupported source type in COpenGLGC");
    }
}

void COpenGLGC::Rasterize(img::CScanlineRasterizer<> &pRasterizer)
{
    std::vector<float> lVertexData;
    lVertexData.reserve(50000);
    pRasterizer.RewindScanlines();
    CGLVertexLineRenderer lRenderer(lVertexData);
    if(mClip)
    {
        img::CIntersectionRenderer<CGLVertexLineRenderer> lIntersectionRenderer(lRenderer,mClipArea);
        pRasterizer.RenderScanlinesSpans(lIntersectionRenderer);
    }
    else
    {
        pRasterizer.RenderScanlines(lRenderer);
    }

    gl::CVertexArray lVertexArray;
    lVertexArray.Bind();
    
    PrepareProgram();
    mVBO.Bind();
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    
    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    glDisable(GL_LINE_SMOOTH);

    int lDone=0;
    while(lDone<lVertexData.size())
    {
        int lNow=std::min(65536/sizeof(float)/6*6,lVertexData.size()-lDone);
        mVBO.SubData(lVertexData.data()+lDone,sizeof(float)*lNow);
        lDone+=lNow;

        glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,3*sizeof(float),(void*)nullptr);
        glVertexAttribPointer(1,1,GL_FLOAT,GL_FALSE,3*sizeof(float),(void*)8);

        glDrawArrays(GL_LINES,0,lNow/3);
    }

    gl::CProgram::UseNone();
    mVBO.Unbind();

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
}

void COpenGLGC::Paint()
{
    ///\todo shortcut. if there is no clipping area, just render a single rectangle.
    ///if there is a clipping area, then just render the clipping area, no intersection stuff
    img::CScanlineRasterizer<> lRasterizer;
    lRasterizer.ClipBox(mExposeRect);
    img::CPath lPath;
    lPath.Rect(mExposeRect);
    lPath.InterpretFlat(lRasterizer);
    
    Rasterize(lRasterizer);
}

void COpenGLGC::DoFill()
{
    img::CScanlineRasterizer<> lRasterizer;
    lRasterizer.ClipBox(mExposeRect);
    Path().InterpretFlat(lRasterizer);

    Rasterize(lRasterizer);
}

void COpenGLGC::DoStroke()
{
    img::CScanlineRasterizer<> lRasterizer;
    lRasterizer.ClipBox(mExposeRect);
    img::CStroker<img::CScanlineRasterizer<>> lStroker(mStroke,mTransform,0.1,lRasterizer);
    lStroker.Stroke(Path());

    Rasterize(lRasterizer);
}

void COpenGLGC::DoClip()
{
    img::CScanlineRasterizer<> lRasterizer;
    lRasterizer.ClipBox(mExposeRect);
    Path().InterpretFlat(lRasterizer);

    lRasterizer.RewindScanlines();
    lRasterizer.RenderToScanlines(mClipArea);
    
    mClipArea.Print();
    mClip=true;
}

void COpenGLGC::DoResetClip()
{
    mClip=false;
}

static const int sMaxGlyphSize=128;

void COpenGLGC::DrawGlyphs(img::PFont pFont,img::TFontIndex pIndex,int *pIndices,int pN,
            const std::vector<img::CTextLayout::SGlyph> &pGlyphs,const CPoint_d &pPos)
{
    //it seems that the cairo path for a cairo_show_glyphs operation is to first call cairo->backend->glyphs.
    //it seems that there is a single backend, with many possible surfaces. this backend calls
    //cairo_gstate_show_text_glyphs, 

    //
    //
    //here starts cairo_gstate_show_text_glyphs
    //
    //

    //do nothing if operator is to keep destination
    ///\todo if(mState.mOp==OP_DEST) return;

    ///\todo check if it is all clipped, and return if it is
    
    img::CScaledFont *lFont=pFont->GetScaledFont(pIndex);

    std::vector<img::CGlyphShow> lGlyphs(pN);

    for(int i=0;i<pN;i++)
    {
        const img::CTextLayout::SGlyph &lGlyph=pGlyphs[pIndices[i]];
        lGlyphs[i].mGlyph=lGlyph.mGlyph;
        lGlyphs[i].mPos=lGlyph.mPos+pPos;
        lGlyphs[i].mPos.x=round(lGlyphs[i].mPos.x);
        lGlyphs[i].mPos.y=round(lGlyphs[i].mPos.y);
    }

    CAutoVector<img::CGlyphShow,64> lTransformedGlyphs(pN);

    int lTransformedCount;
    TransformGlyphs(lFont,lGlyphs,lTransformedGlyphs.data(),lTransformedCount);

    if(!lTransformedCount) return;

    ///\todo:there is an interesting little function in cairo called _reduce_op, which calculates the effective
    //operator from the operator and some other state
    //EOperator lEffOp=mState.mOperator;
    
    ///\todo:now we should set the pattern to use, depending on the operator and the actual source pattern

    ///\todo:cairo uses fill path instead of show glyphs for really huge sizes, to not put too much pressure in the
    //cache, and it would use it for not so huge sizes if their rasterizer was as fast as freetype's
    //this means calling a path function, and a fill function... otherwise, continue
    

    //now it calls _cairo_surface_show_text_glyphs
    
    //
    //
    //here ends cairo_gstate_show_text_glyphs
    //
    //

    //
    //
    //here begins _cairo_surface_show_text_glyphs
    //
    //

    ///\todo: there is some fnction called nothing_to_do, which based on surface, op and source decides whether
    //there is nothing to do... check it out!

    ///\todo
    //_cairo_surface_begin_modification (surface);

    ///\todo
//     if (_cairo_surface_has_device_transform (surface) &&
//         ! _cairo_matrix_is_integer_translation (&surface->device_transform, NULL, NULL))
//     {
//         cairo_font_options_t font_options;
//         cairo_matrix_t dev_ctm, font_matrix;
// 
//         cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix);
//         cairo_scaled_font_get_ctm (scaled_font, &dev_ctm);
//         cairo_matrix_multiply (&dev_ctm, &dev_ctm, &surface->device_transform);
//         cairo_scaled_font_get_font_options (scaled_font, &font_options);
//         dev_scaled_font = cairo_scaled_font_create (cairo_scaled_font_get_font_face (scaled_font),
//                                                     &font_matrix,
//                                                     &dev_ctm,
//                                                     &font_options);
//     }
//     status = cairo_scaled_font_status (dev_scaled_font);
//     if (unlikely (status))
//         return _cairo_surface_set_error (surface, status);
// 
//     status = CAIRO_INT_STATUS_UNSUPPORTED;
// 

    //now, if we have clusters, we call show_text_glyphs for the surface .. which takes the text into account. I guess this makes
    //sense for PDFs, where the actual texts ends up in the result.
    //here, I think we should just use show_glyphs, and we'll do that
    
    //
    //
    //here ends _cairo_surface_show_text_glyphs
    //
    //

    //then cairo_gl_surface_glyphs calls cairo_compositor_glyphs
    
    //
    //
    //here begins _cairo_compositor_glyphs
    //
    //

    ///\todo: this seems to initialize some rectangle structure to keep track of modified areas
    
//     cairo_composite_rectangles_t extents;
//     cairo_bool_t overlap;
//     cairo_int_status_t status;
// 
//     TRACE ((stderr, "%s\n", __FUNCTION__));
//     status = _cairo_composite_rectangles_init_for_glyphs (&extents, surface,
//                                                           op, source,
//                                                           scaled_font,
//                                                           glyphs, num_glyphs,
//                                                           clip, &overlap);

    //then it actually calls the glyphs function from the compositor
    //which will do the real work... it passes it the extents structure so that it can say which parts were modified
    
    
    //
    //
    //here begins _cairo_gl_composite_glyphs (which just calls render_glyphs)
    //
    //

    //it performs some check as to whether a mask should be used (seems to be only when format is ARGB32)
    //otherwise, it just calls render_glyphs... which appears here:

    ///\todo: it starts by calling cairo_gl_context_acquire, which locks some mutexes and calls glXMakeCurrent.
    ///we can probably assume for now that this is already done

    ///\todo:it calls cairo_gl_composite_init, which sets up some composition setup structure

    ///\todo:it sets the source operand on the setup structure (to solid white if there is nothing)
    
    if(!mVBOMap) mVBOMap=mVBO.Map<float>();
    
    gl::CGlyphCache *lCache=GetGlyphCache();
    img::EImageFormat lLastFormat=img::FORMAT_INVALID;

    int lEmitted=0;
    
    for(int i=0;i<pN;i++)
    {
        img::CScaledGlyph *lGlyph=lFont->LookupGlyph(lGlyphs[i].mGlyph,img::GLYPHDATA_IMAGE);
        if(!lGlyph->mImage) continue;

        img::CAnyImage *lImage=lGlyph->mImage;

        if(lImage->Size().x>sMaxGlyphSize||lImage->Size().y>sMaxGlyphSize)
        {
            throw XInvalid("glyph too big to show");
        }

        if(lImage->Format()!=lLastFormat&&lLastFormat)
        {
            if(lLastFormat==img::FORMAT_INVALID)
                lLastFormat=lImage->Format();
            else
                msg_warn() << "format problem!!!\n";
            //TODO:flush emitted glyphs
//            lGlyphCache=GetGlyphCache(lImage->GetFormat());
//            glActiveTexture(GL_TEXTURE1);
//            glBindTexture(GL_TEXTURE_2D,lGlyphCache->GetTexture());

        }

        if(!lGlyph->mCacheEntry)
        {
            //TODO: check if it fails because it is full. Then flush and whatnot
            lCache->AddGlyph(lGlyph);
        }

        //emit the glyph
        CRect_f lTex=lCache->GetRect(lGlyph);
        CRect_f lPos(CPoint_f(lGlyphs[i].mPos)+CPoint_f(lGlyph->mMetrics.mBearing.x,-lGlyph->mMetrics.mBearing.y),
                     CSize_f(lGlyph->mBBox.Size()));

        mVBOMap << lPos.TopLeft() << lTex.TopLeft();
        mVBOMap << lPos.TopRight() << lTex.TopRight();
        mVBOMap << lPos.BottomLeft() << lTex.BottomLeft();

        mVBOMap << lPos.TopRight() << lTex.TopRight();
        mVBOMap << lPos.BottomRight() << lTex.BottomRight();
        mVBOMap << lPos.BottomLeft() << lTex.BottomLeft();

        lEmitted++;
    }

    mVBO.Unmap(mVBOMap);

    if(!lEmitted) return;
    
    mGlyphProgram.Use();
    mGlyphCache->Texture().Bind();
    
    int lNumVertices=lEmitted*6;

    GLuint tonto;
    glGenVertexArrays(1,&tonto);
    glBindVertexArray(tonto);
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,4*sizeof(float),(void*)nullptr);
    glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,4*sizeof(float),(void*)8);
    glVertexAttrib3d(2,Source().Color().r,Source().Color().g,Source().Color().b);

    glDrawArrays(GL_TRIANGLES,0,lNumVertices);

    mGlyphProgram.UseNone();
    mVBO.Unbind();
    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glBindVertexArray(0);
    glDeleteVertexArrays(1,&tonto);
}

void COpenGLGC::SetExposeSize(const CSize_d &pSize)
{
    mat::CMat23d lMat={2.0/pSize.x,0         ,-1.0,
                       0        ,-2.0/pSize.y,1.0};
    mExposeRect={nZero,pSize};
    mGlyphProgram.Use();
    mGlyphTransformation.SetValue(lMat);
    mGlyphProgram.UseNone();
    mSolidProgram.Use();
    mSolidTransformation.SetValue(lMat);
    mSolidProgram.UseNone();
    mLinearProgram.Use();
    uLinearTransformation.SetValue(lMat);
    mLinearProgram.UseNone();
    mRadialProgram.Use();
    uRadialTransformation.SetValue(lMat);
    mRadialProgram.UseNone();
    mImageProgram.Use();
    mImageTransformation.SetValue(lMat);
    mImageProgram.UseNone();
}

void COpenGLGC::RenderImage(const CPoint_d &pPos,const img::CAnyView &pView)
{
    CTexture lTexture;
    lTexture.Create2D(pView);
    lTexture.Bind();

    GLuint tonto;
    glGenVertexArrays(1,&tonto);
    glBindVertexArray(tonto);

    mImageProgram.Use();
    std::vector<float> lVertexData;
    lVertexData.push_back(pPos.x);
    lVertexData.push_back(pPos.y);
    lVertexData.push_back(0);
    lVertexData.push_back(0);
    lVertexData.push_back(pPos.x+pView.Width());
    lVertexData.push_back(pPos.y);
    lVertexData.push_back(1);
    lVertexData.push_back(0);
    lVertexData.push_back(pPos.x);
    lVertexData.push_back(pPos.y+pView.Height());
    lVertexData.push_back(0);
    lVertexData.push_back(1);
    lVertexData.push_back(pPos.x);
    lVertexData.push_back(pPos.y+pView.Height());
    lVertexData.push_back(0);
    lVertexData.push_back(1);
    lVertexData.push_back(pPos.x+pView.Width());
    lVertexData.push_back(pPos.y);
    lVertexData.push_back(1);
    lVertexData.push_back(0);
    lVertexData.push_back(pPos.x+pView.Width());
    lVertexData.push_back(pPos.y+pView.Height());
    lVertexData.push_back(1);
    lVertexData.push_back(1);
    mVBO.Bind();
    mVBO.SubData(lVertexData.data(),sizeof(float)*lVertexData.size());

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,4*sizeof(float),(void*)nullptr);
    glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,4*sizeof(float),(void*)8);

    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    glDrawArrays(GL_TRIANGLES,0,6);

    gl::CProgram::UseNone();
    mVBO.Unbind();

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glBindVertexArray(0);
    glDeleteVertexArrays(1,&tonto);
    lTexture.UnBind();
}

void COpenGLGC::Composite(const CPoint_d &pDstPoint,const CSize_d &pDstSize,const img::CPicture &pSrc,
                          const CPoint_d &pSrcPoint,const CSize_d &pSrcSize,const img::CPicture &pMask,
                          const CPoint_d& /*pMaskPoint*/,const CSize_d& /*pMaskSize*/,img::EExtend /*pExtend*/)
{
    CTexture lTexture;
    lTexture.Create2D(pSrc.Image());
    lTexture.Bind();

    GLuint tonto;
    glGenVertexArrays(1,&tonto);
    glBindVertexArray(tonto);

    CSize_d lSrcSize=pSrcSize;
    if(lSrcSize.Undefined())
        lSrcSize=pSrc.Size();
    
    mImageProgram.Use();
    std::vector<float> lVertexData;
    lVertexData.push_back(pDstPoint.x);
    lVertexData.push_back(pDstPoint.y);
    lVertexData.push_back(pSrcPoint.x/pSrc.Size().x);
    lVertexData.push_back(pSrcPoint.y/pSrc.Size().y);
    lVertexData.push_back(pDstPoint.x+pDstSize.x);
    lVertexData.push_back(pDstPoint.y);
    lVertexData.push_back((pSrcPoint.x+lSrcSize.x)/pSrc.Size().x);
    lVertexData.push_back(pSrcPoint.y/pSrc.Size().y);
    lVertexData.push_back(pDstPoint.x);
    lVertexData.push_back(pDstPoint.y+pDstSize.y);
    lVertexData.push_back(pSrcPoint.x/pSrc.Size().x);
    lVertexData.push_back((pSrcPoint.y+lSrcSize.y)/pSrc.Size().y);
    lVertexData.push_back(pDstPoint.x);
    lVertexData.push_back(pDstPoint.y+pDstSize.y);
    lVertexData.push_back(pSrcPoint.x/pSrc.Size().x);
    lVertexData.push_back((pSrcPoint.y+lSrcSize.y)/pSrc.Size().y);
    lVertexData.push_back(pDstPoint.x+pDstSize.x);
    lVertexData.push_back(pDstPoint.y);
    lVertexData.push_back((pSrcPoint.x+lSrcSize.x)/pSrc.Size().x);
    lVertexData.push_back(pSrcPoint.y/pSrc.Size().y);
    lVertexData.push_back(pDstPoint.x+pDstSize.x);
    lVertexData.push_back(pDstPoint.y+pDstSize.y);
    lVertexData.push_back((pSrcPoint.x+lSrcSize.x)/pSrc.Size().x);
    lVertexData.push_back((pSrcPoint.y+lSrcSize.y)/pSrc.Size().y);
    mVBO.Bind();
    mVBO.SubData(lVertexData.data(),sizeof(float)*lVertexData.size());

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,4*sizeof(float),(void*)nullptr);
    glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,4*sizeof(float),(void*)8);

    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    glDrawArrays(GL_TRIANGLES,0,6);

    gl::CProgram::UseNone();
    mVBO.Unbind();

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glBindVertexArray(0);
    glDeleteVertexArrays(1,&tonto);
    lTexture.UnBind();
}

void COpenGLGC::MakeCurrent(img::CSurface *pSurface)
{
    mContext->MakeCurrent(pSurface);

    if(!mVBO)
    {
        mVBO.Create(65536);
        mVBO.Unbind();
    }

    if(!mGlyphProgram)
    {
        gl::CShader lGlyphVertex(gl::CShader::VERTEX,shaders::sGlyphVertex);
        lGlyphVertex.Compile();
        gl::CShader lGlyphFragment(gl::CShader::FRAGMENT,shaders::sGlyphFragment);
        lGlyphFragment.Compile();
        mGlyphProgram.Create();
        mGlyphProgram.Attach(lGlyphVertex);
        mGlyphProgram.Attach(lGlyphFragment);
        mGlyphProgram.BindFragmentData(0,"oFragColor");
        mGlyphProgram.BindAttribLocation(0,"iVertex");
        mGlyphProgram.BindAttribLocation(1,"iTex");
        mGlyphProgram.BindAttribLocation(2,"iColor");
        mGlyphProgram.Link();

        mGlyphTransformation=mGlyphProgram.GetUniform("uTrans");
    }

    if(!mSolidProgram)
    {
        gl::CShader lSolidVertex(gl::CShader::VERTEX,shaders::sSolidVertex);
        lSolidVertex.Compile();
        gl::CShader lSolidFragment(gl::CShader::FRAGMENT,shaders::sSolidFragment);
        lSolidFragment.Compile();
        mSolidProgram.Create();
        mSolidProgram.Attach(lSolidVertex);
        mSolidProgram.Attach(lSolidFragment);
        mSolidProgram.BindFragmentData(0,"oFragColor");
        mSolidProgram.Link();

        mSolidTransformation=mSolidProgram.GetUniform("uTrans");
        mSolidColor=mSolidProgram.GetUniform("uColor");
    }

    if(!mLinearProgram)
    {
        gl::CShader lLinearVertex(gl::CShader::VERTEX,shaders::sLinearVertex);
        lLinearVertex.Compile();
        gl::CShader lLinearFragment(gl::CShader::FRAGMENT,shaders::sLinearFragment);
        lLinearFragment.Compile();
        mLinearProgram.Create();
        mLinearProgram.Attach(lLinearVertex);
        mLinearProgram.Attach(lLinearFragment);
        mLinearProgram.BindFragmentData(0,"oFragColor");
        mLinearProgram.Link();

        uLinearTransformation=mLinearProgram.GetUniform("uTrans");
        uLinearOrigin=mLinearProgram.GetUniform("uGradientOrigin");
        uLinearDirection=mLinearProgram.GetUniform("uGradientDirection");
        uLinearLength=mLinearProgram.GetUniform("uGradientLength");

        gl::CShader lRadialFragment(gl::CShader::FRAGMENT,shaders::sRadialFragment);
        lRadialFragment.Compile();
        mRadialProgram.Create();
        mRadialProgram.Attach(lLinearVertex);
        mRadialProgram.Attach(lRadialFragment);
        mRadialProgram.BindFragmentData(0,"oFragColor");
        mRadialProgram.Link();

        uRadialTransformation=mRadialProgram.GetUniform("uTrans");
        uRadial1=mRadialProgram.GetUniform("uGradient1");
        uRadialD=mRadialProgram.GetUniform("uGradientD");
        uRadialA=mRadialProgram.GetUniform("uGradientA");
    }

    if(!mImageProgram)
    {
        mImageProgram.CreateAndAttach(shaders::sImageVertex,shaders::sImageFragment);
        mImageProgram.BindFragmentData(0,"oFragColor");
        mImageProgram.BindAttribLocation(0,"iVertex");
        mImageProgram.BindAttribLocation(1,"iCoverage");
        mImageProgram.Link();

        mImageTransformation=mImageProgram.GetUniform("uTrans");
    }
}

bool COpenGLGC::IsCurrent()
{
    return mContext->IsCurrent();
}

void COpenGLGC::SwapBuffers()
{
    mContext->SwapBuffers();
}

void COpenGLGC::SwapBuffers(const CRegion &pRegion,int pH)
{
    glReadBuffer(GL_BACK);
    glDrawBuffer(GL_FRONT);

    for(const CRect_i &lR : pRegion)
    {
        CRect_i lT(lR.l,pH-lR.b,lR.r,pH-lR.t);
        glBlitFramebuffer(lT.l,lT.t,lT.r,lT.b,lT.l,lT.t,lT.r,lT.b,GL_COLOR_BUFFER_BIT,GL_NEAREST);
    }
    
    glDrawBuffer(GL_BACK);
    glFlush();
}

gl::CGlyphCache *COpenGLGC::GetGlyphCache()
{
    if(!mGlyphCache)
        mGlyphCache=new gl::CGlyphCache;
    return mGlyphCache;
}

/**
* _cairo_gstate_transform_glyphs_to_backend:
* @gstate: a #cairo_gstate_t
* @glyphs: the array of #cairo_glyph_t objects to be transformed
* @num_glyphs: the number of elements in @glyphs
* @transformed_glyphs: a pre-allocated array of at least @num_glyphs
* #cairo_glyph_t objects
* @num_transformed_glyphs: the number of elements in @transformed_glyphs
* after dropping out of bounds glyphs, or %nullptr if glyphs shouldn't be
* dropped
*
* Transform an array of glyphs to backend space by first adding the offset
* of the font matrix, then transforming from user space to backend space.
* The result of the transformation is placed in @transformed_glyphs.
*
* This also uses information from the scaled font and the surface to
* cull/drop glyphs that will not be visible.
**/
void COpenGLGC::TransformGlyphs(img::CScaledFont *pFont,const std::vector<img::CGlyphShow> &pGlyphs,
                                img::CGlyphShow *pTransformed,int &pTransformedCount)
{
    //TODO:once we have implemented clipping, we should do some here

    //for now:
    pTransformedCount=pGlyphs.size(); //we don't clip any glyph

    const mat::C2DTransform &lTM=Transform();
    const mat::C2DTransform &lFM=pFont->GetFontMatrix();

    if (lTM.IsIdentity()&&lFM.F(0,2)==0&&lFM.F(1,2)==0) //identity transformation, no translation in font matrix
    {
        //just copy the glyphs
        memcpy(pTransformed,pGlyphs.data(),pGlyphs.size()*sizeof(img::CGlyphShow));
    }
    else if (lTM.IsTranslation()) //only a translation
    {
        CPoint_d lT(lFM.F(0,2)+lTM.F(0,2),lFM.F(1,2)+lTM.F(1,2));

        for(int i=0;i<pGlyphs.size();i++)
        {
            pTransformed[i].mGlyph=pGlyphs[i].mGlyph;
            pTransformed[i].mPos=pGlyphs[i].mPos+lT;
        }
    }
    else //we need to transform the points
    {
        mat::CMat23d lCombined=lTM*mat::translation_2d({lFM.F(0,2),lFM.F(1,2)});

        for(int i=0;i<pGlyphs.size();i++)
        {
            pTransformed[i].mGlyph=pGlyphs[i].mGlyph;
            pTransformed[i].mPos=lCombined*pGlyphs[i].mPos;
        }
    }
}

/*namespace gl*/ } /*namespace buola*/ }
